﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Light.Framework;
using Light.Utility;

namespace Light.Framework.Castle
{
    /// <summary>
    /// SQL语句转换器
    /// </summary>
    public class SQLConverter
    {
        private static readonly Database dbms;

        static SQLConverter()
        {
            dbms = SQLManager.CreateExecuter(false).DBMS;
        }

        public static string Replace(Type type, string sql, string num)
        {
            return Replace(type, sql, num, new object[0]);
        }

        public static string Replace(Type type, string sql, string num, params object[] values)
        {
            string rsql = null;

            if ((dbms != Database.Firebird) && !String.IsNullOrEmpty(num) && (type != null))
            {
                string name = String.Format("Sql_{0}", num.Replace(" ", String.Empty));

                Assembly asm = type.Assembly;

                //key根据方法名称缓存SQL语句会有问题，因为参数不同会产生不同的SQL,因此把参数值也作为键值的一部份
                //string key = String.Format("{0}_{1}", asm.GetName().Name, name);

                StringBuilder methodParams = new StringBuilder();
                foreach (var item in values)
                {
                    if (item != null)
                    {
                        methodParams.Append(item.ToString());
                    }
                }
                string key = String.Format("{0}_{1}_{2}", asm.GetName().Name, name, methodParams.ToString());

                object obj = ServerApplication.Cache.Get(key);

                if (obj == null)
                {
                    foreach (Type t in asm.GetTypes())  //GetExportedTypes();
                    {
                        if (t == type)
                            continue;

                        if (t.IsClass && t.IsSealed && !t.IsVisible && !t.IsAbstract && (t.BaseType).BaseType != typeof(System.Delegate))
                        {
                            object objectHandle = Activator.CreateInstance(t);
                            if (objectHandle != null)
                            {
                                BindingFlags flags = (BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance);
                                MethodInfo method;

                                try
                                {
                                    method = t.GetMethod(name, flags);
                                }
                                catch (AmbiguousMatchException)
                                {
                                    method = t.GetMethod(name, (flags | BindingFlags.DeclaredOnly));
                                }

                                if (method != null &&
                                    !method.IsAbstract &&
                                    method.ReturnParameter.ParameterType == typeof(String))
                                {
                                    ParameterInfo[] pi = method.GetParameters();
                                    if (pi.Length > 0 && pi[0].ParameterType == typeof(Database))
                                    {
                                        List<object> parms = new List<object>();
                                        parms.Add(dbms);
                                        if (values != null && values.Length != 0)
                                            parms.AddRange(values);

                                        obj = method.Invoke(objectHandle, parms.ToArray());
                                        if (obj != null)
                                            rsql = (obj as string);

                                        break;
                                    }
                                }
                            }
                        }
                    }

                    if (rsql != null)
                        ServerApplication.Cache.Add(key, rsql);
                    else
                        ServerApplication.Cache.Add(key, sql);
                }
                else
                {
                    rsql = (obj as string);
                }
            }

            return (String.IsNullOrEmpty(rsql) ? sql : rsql);
        }
    }
}
